<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Three.js实现光照阴影</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
</body>
<script type="importmap">
    {
        "imports": {
            "three": "./js/threejs/three.module.js",
            "three/addons/": "./js/threejs/jsm/"
        }
    }
</script>

<script type="module">
    import * as THREE from 'three';
    import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
    import { DragControls } from 'three/addons/controls/DragControls.js';
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
    import { TransformControls } from 'three/addons/controls/TransformControls.js';

    // 创建div标签 container图层
    let container = document.createElement('div');
    // 向body中插入一个dom标签元素
    document.body.appendChild(container);

    // 1. 创建空白场景
    let scene = new THREE.Scene();

    // 2. 创建相机
    /**
     * width    窗口宽度
     * height   窗口高度
     * k        窗口宽高比
     * s        三维场景显示范围控制系数，系数越大，显示的范围越大
     */
    const width = window.innerWidth;
    const height = window.innerHeight;
    const k = width / height;
    const s = 100;

    //创建相机对象
    /**
     * 正视投影
     * left     摄像机视锥体左侧面
     * right    摄像机视锥体右侧面
     * top      摄像机视锥体上侧面
     * bottom   摄像机视锥体下侧面
     * near     摄像机视锥体近端面
     * far      摄像机视锥体远端面
     */
    var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000); //正视投影

    /**
     * 透视投影 见./pictures/透视投影-视锥体.png
     * PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
     * fov      摄像机视锥体垂直视野角度
     * aspect   摄像机视锥体长宽比
     * near     摄像机视锥体近端面
     * far      摄像机视锥体远端面
     */
    // const camera = new THREE.PerspectiveCamera(90, width / height, 1, 500);
    camera.position.set(50, 50, 50); // 设置相机位置xyz
    // camera.lookAt(scene.position); //设置相机方向(指向的场景对象)position

    // 3. 创建渲染器
    let renderer = new THREE.WebGLRenderer({
        antialias: true, // 是否执行抗锯齿
    });
    renderer.setPixelRatio(window.devicePixelRatio); // 设置设备像素比率。通常用于HiDPI设备，以防止输出画布模糊。
    renderer.setSize(width, height); // 设置渲染器大小 ***
    renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
    renderer.shadowMap.enabled = true;
    container.appendChild(renderer.domElement);

    var geometry6 = new THREE.CylinderGeometry(3, 3, 10, 50);  // THREE.CylinderGeometry构造圆柱体
    var material6 = new THREE.MeshLambertMaterial({
        color: 0xFFFF00,
        wireframe: false   //线框，默认关闭
    });  //THREE.MeshLambertMaterial高级材质，构造类似木头、石头等不光滑的表面
    var mesh6 = new THREE.Mesh(geometry6, material6);  // 网状 Mesh的构造函数是这样的：Mesh( geometry, material ) geometry是它的形状，material是它的材质
    mesh6.position.set(20, 0, 0);
    ;
    scene.add(mesh6);

    // 4. 材质
    // 金属材质
    const geometry = new THREE.BoxGeometry(2, 5, 6); //创建一个立方体几何对象Geometry

    // 材质对象Material
    const material = new THREE.MeshLambertMaterial({
        color: 0x7777ff,
        specular: 0x7777ff,
        shininess: 30
    });

    const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
    mesh.castShadow = true;
    mesh.position.set(-10, 3, 3); // 设置物体位置
    scene.add(mesh); //网格模型添加到场景中

    // 圆柱网格模型
    const geometry3 = new THREE.CylinderGeometry(3, 3, 10, 50);
    const material3 = new THREE.LineDashedMaterial({
        color: 0xffffff,
        wireframe: false   //线框，默认关闭
    });
    const mesh3 = new THREE.Mesh(geometry3, material3); //网格模型对象Mesh
    // mesh3.translateX(120); //球体网格模型沿Y轴正方向平移120
    mesh3.position.set(12, 0, 0);//设置mesh3模型对象的xyz坐标为120,0,0
    mesh3.castShadow = true; // 对象是否渲染到阴影贴图中，默认值为false
    scene.add(mesh3); //

    // 创建物体2 网格Lambert材质
    const geometry2 = new THREE.BoxGeometry(2, 5, 6);
    const material2 = new THREE.MeshLambertMaterial({
        color: 0xff00ff,  //颜色 16进制
        opacity: 0.9,      //透明度 0-1
        transparent: true, //开启透明度，默认关闭
        wireframe: false   //线框，默认关闭
    }); //材质对象Material
    const mesh2 = new THREE.Mesh(geometry2, material2);
    mesh2.castShadow = true; // 对象是否渲染到阴影贴图中，默认值为false
    mesh2.position.set(1, 3, 3); // 设置物体位置
    scene.add(mesh2); // 添加到场景中
    camera.lookAt(mesh2.position); //设置相机方向(指向的场景对象)position


    // 创建物体4
    const geometry4 = new THREE.BoxGeometry(2, 5, 6); //创建一个球体几何对象
    const material4 = new THREE.MeshLambertMaterial({
        color: 0xff00ff,  //颜色 16进制
        opacity: 0.9,      //透明度 0-1
        transparent: true, //开启透明度，默认关闭
        wireframe: true   //线框，默认关闭
    }); //材质对象Material
    const mesh4 = new THREE.Mesh(geometry4, material4);
    mesh4.castShadow = true; // 对象是否渲染到阴影贴图中，默认值为false
    mesh4.position.set(-20, 3, 3); // 设置物体位置
    scene.add(mesh4); // 添加到场景中

    // 创建平面
    const planeGeometry = new THREE.PlaneGeometry(300, 300); // 生成平面几何
    const planeMaterial = new THREE.MeshLambertMaterial({
        // 生成材质
        color: 0xffffff,
    });
    const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial); // 生成平面网格
    planeMesh.receiveShadow = true; // 设置平面网格为接受阴影的投影面
    planeMesh.rotation.x = -Math.PI / 2; //绕X轴旋转90度
    scene.add(planeMesh); // 添加到场景中

    //创建坐标轴
    const axesHelper = new THREE.AxesHelper( 100 );
    axesHelper.position.set(0,0,0)
    scene.add( axesHelper );

    //创建光源
    const light = new THREE.PointLight(0xffffff, 2); // 点光源 颜色为白色，强度为
    light.position.set(40, 40, 20); // 设置灯源位置
    light.castShadow = true; // 允许生成阴影
    scene.add(light); // 添加到场景中

    // // 聚光光源
    // var spotLight = new THREE.SpotLight(0xffffff);
    // // 设置聚光光源位置
    // spotLight.position.set(20, 20, 20);
    // // 聚光灯光源指向网格模型mesh2
    // spotLight.target = mesh2;
    // // 设置聚光光源发散角度
    // spotLight.angle = Math.PI / 6
    // spotLight.castShadow = true; // 允许生成阴影
    // scene.add(spotLight);//光对象添加到scene场景中

    //创建环境光
    const ambient = new THREE.AmbientLight(0x444444);
    scene.add(ambient);

    // 创建控制器
    let controls = new OrbitControls(camera, renderer.domElement);
    // controls.enabled = false;
    const transformControls = new TransformControls(camera, renderer.domElement);
    transformControls.setSize(0.5);
    transformControls.setMode("translate");//

    //当除了移动控件的其他控件触发时，禁止移动控件
    transformControls.addEventListener('dragging-changed', function (event) {

        controls.enabled = !event.value;

    });
    scene.add(transformControls);
    transformControls.attach(mesh2);

    //键盘响应
    window.addEventListener('keydown', function (event) {

        switch (event.keyCode) {

            case 82: // R
                transformControls.setMode('rotate');
                break;
            case 83: // S
                transformControls.setMode('scale');
                break;
            case 84: // T
                transformControls.setMode('translate');
                break;
            case 187:
            case 107: // +, =, num+
                transformControls.setSize(transformControls.size + 0.1);
                break;
            case 189:
            case 109: // -, _, num-
                transformControls.setSize(Math.max(transformControls.size - 0.1, 0.1));
                break;
        }

    });

    animate();

    function animate() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
    }
</script>
</html>
